# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import time
from openerp.osv import fields
from openerp.osv import osv
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
import openerp.addons.decimal_precision as dp
from openerp import workflow


class res_company(osv.Model):
    _inherit = "res.company"
    _columns = {
                'sale_note':fields.text(u'默认条款和条件',translate=True,help = u'订单默认条款和条件'),
                }
    
class yz_sale_order(osv.osv):
    _name = 'yz.sale.order'
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _description = "销售订单"
    _track = {
        'state': {
            'sale.mt_order_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state in ['manual'],
            'sale.mt_order_sent': lambda self, cr, uid, obj, ctx=None: obj.state in ['sent']
        },
    }
       
    def _make_invoice(self,cr,uid,order,lines,context=None):
        inv_obj = self.pool.get('account.invoice')
        obj_invoice_line = self.pool.get('account.invoice.line')
        if context is None:
            context = {}
        invoiced_sale_line_ids = self.pool.get('sale.order.line').search(cr,uid,[('order_id','=',order.id),('invoiced','=',True)],context=context)
        from_line_invoice_ids = []
        for invoiced_sale_line_id in self.pool.get('sale.order.line').browse(cr, uid, invoiced_sale_line_ids, context=context):
            for invoice_line_id in invoiced_sale_line_id.invoice_lines:
                if invoice_line_id.invoice_id.id not in from_line_invoice_ids:
                    from_line_invoice_ids.append(invoice_line_id.invoice_id.id)
        
        for preinv in order.invoice_ids:
            if preinv.state not in ('cancel',) and preinv.id not in from_line_invoice_ids:
                for preline in preinv.invoice_line:
                    inv_line_id = obj_invoice_line.copy(cr, uid, preline.id, {'invoice_id': False, 'price_unit': -preline.price_unit})
                    lines.append(inv_line_id)
        inv = self._prepare_invoice(cr, uid, order, lines, context=context)
        inv_id = inv_obj.create(cr, uid, inv, context=context)
        data = inv_obj.onchange_payment_term_date_invoice(cr, uid, [inv_id], inv['payment_term'], time.strftime(DEFAULT_SERVER_DATE_FORMAT))
        if data.get('value', False):
            inv_obj.write(cr, uid, [inv_id], data['value'], context=context)
        inv_obj.button_compute(cr, uid, [inv_id])
        return inv_id
        
    def action_invoice_create(self, cr, uid, ids, grouped=False, states=None, date_invoice = False, context=None):
        if states is None:
            states = ['confirmed', 'done', 'exception']
        res = False
        invoices = {}
        invoice_ids = []
        invoice = self.pool.get('account.invoice')
        obj_sale_order_line = self.pool.get('sale.order.line')
        partner_currency = {}
        # If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
        # last day of the last month as invoice date
        if date_invoice:
            context = dict(context or {}, date_invoice=date_invoice)
        for o in self.browse(cr, uid, ids, context=context):
            currency_id = o.pricelist_id.currency_id.id
            if (o.customer_id.id in partner_currency) and (partner_currency[o.customer_id.id] <> currency_id):
                raise osv.except_osv(
                    _('Error!'),
                    _('You cannot group sales having different currencies for the same partner.'))

            partner_currency[o.customer_id.id] = currency_id
            lines = []
            for line in o.order_line:
                if line.invoiced:
                    continue
                elif (line.state in states):
                    lines.append(line.id)
            created_lines = obj_sale_order_line.invoice_line_create(cr, uid, lines)
            if created_lines:
                invoices.setdefault(o.partner_invoice_id.id or o.customer_id.id, []).append((o, created_lines))
        if not invoices:
            for o in self.browse(cr, uid, ids, context=context):
                for i in o.invoice_ids:
                    if i.state == 'draft':
                        return i.id
        for val in invoices.values():
            if grouped:
                res = self._make_invoice(cr, uid, val[0][0], reduce(lambda x, y: x + y, [l for o, l in val], []), context=context)
                invoice_ref = ''
                origin_ref = ''
                for o, l in val:
                    invoice_ref += (o.client_order_ref or o.name) + '|'
                    origin_ref += (o.origin or o.name) + '|'
                    self.write(cr, uid, [o.id], {'state': 'progress'})
                    cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (o.id, res))
                    self.invalidate_cache(cr, uid, ['invoice_ids'], [o.id], context=context)
                #remove last '|' in invoice_ref
                if len(invoice_ref) >= 1:
                    invoice_ref = invoice_ref[:-1]
                if len(origin_ref) >= 1:
                    origin_ref = origin_ref[:-1]
                invoice.write(cr, uid, [res], {'origin': origin_ref, 'name': invoice_ref})
            else:
                for order, il in val:
                    res = self._make_invoice(cr, uid, order, il, context=context)
                    invoice_ids.append(res)
                    self.write(cr, uid, [order.id], {'state': 'progress'})
                    cr.execute('insert into sale_order_invoice_rel (order_id,invoice_id) values (%s,%s)', (order.id, res))
                    self.invalidate_cache(cr, uid, ['invoice_ids'], [order.id], context=context)
        return res
        
    def action_invoice_cancel(self, cr, uid, ids, context=None):
            self.write(cr, uid, ids, {'state': 'invoice_except'}, context=context)
            return True
        
    def action_invoice_end(self, cr, uid, ids, context=None):
        for this in self.browse(cr, uid, ids, context=context):
            for line in this.order_line:
                if line.state == 'exception':
                    line.write({'state': 'confirmed'})
            if this.state == 'invoice_except':
                this.write({'state': 'progress'})
        return True

    def action_quotation_send(self, cr, uid, ids, context=None):
        '''
        This function opens a window to compose an email, with the edi sale template message loaded by default
        '''
        assert len(ids) == 1, 'This option should only be used for a single id at a time.'
        ir_model_data = self.pool.get('ir.model.data')
        try:
            template_id = ir_model_data.get_object_reference(cr, uid, 'yz_sale', 'email_template_edi_sale')[1]
        except ValueError:
            template_id = False
        try:
            compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
        except ValueError:
            compose_form_id = False 
        ctx = dict()
        ctx.update({
            'default_model': 'yz.sale.order',
            'default_res_id': ids[0],
            'default_use_template': bool(template_id),
            'default_template_id': template_id,
            'default_composition_mode': 'comment',
            'mark_so_as_sent': True
        })
        return {
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'mail.compose.message',
            'views': [(compose_form_id, 'form')],
            'view_id': compose_form_id,
            'target': 'new',
            'context': ctx,
        }

    def print_quotation(self, cr, uid, ids, context=None):
        '''
        This function prints the sales order and mark it as sent, so that we can see more easily the next step of the workflow
        '''
        assert len(ids) == 1, 'This option should only be used for a single id at a time'
        self.signal_workflow(cr, uid, ids, 'quotation_sent')
        return self.pool['report'].get_action(cr, uid, ids, 'sale.report_saleorder', context=context)
    
    def action_button_confirm(self, cr, uid, ids, context=None):
        assert len(ids) == 1, 'This option should only be used for a single id at a time.'
        self.signal_workflow(cr, uid, ids, 'order_confirm')
        return True

    def action_wait(self,cr,uid,ids,context=None):
        context = context or {}
        for o in self.browse(cr,uid,ids):
            if not o.order_line:
                raise  osv.except_osv(_('Error!'),_('You cannot confirm a sales order which has no line.'))
            noprod = self.test_no_product(cr,uid,o,context)
            if (o.order_policy == 'manual') or noprod:
                self.write(cr, uid, [o.id], {'state': 'manual', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
            else:
                self.write(cr, uid, [o.id], {'state': 'progress', 'date_confirm': fields.date.context_today(self, cr, uid, context=context)})
            self.pool.get('sale.order.line').button_confirm(cr, uid, [x.id for x in o.order_line])
        return True

    def action_done(self, cr, uid, ids, context=None):
        for order in self.browse(cr, uid, ids, context=context):
            self.pool.get('sale.order.line').write(cr, uid, [line.id for line in order.order_line], {'state': 'done'}, context=context)
        return self.write(cr, uid, ids, {'state': 'done'}, context=context)

    #study
    def manual_invoice(self, cr, uid, ids, context=None):
        """ create invoices for the given sales orders (ids), and open the form
            view of one of the newly created invoices
        """
        mod_obj = self.pool.get('ir.model.data')
        
        # create invoices through the sales orders' workflow
        inv_ids0 = set(inv.id for sale in self.browse(cr, uid, ids, context) for inv in sale.invoice_ids)
        self.signal_workflow(cr, uid, ids, 'manual_invoice')
        inv_ids1 = set(inv.id for sale in self.browse(cr, uid, ids, context) for inv in sale.invoice_ids)
        # determine newly created invoices
        new_inv_ids = list(inv_ids1 - inv_ids0)

        res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')
        res_id = res and res[1] or False,

        return {
            'name': _('Customer Invoices'),
            'view_type': 'form',
            'view_mode': 'form',
            'view_id': [res_id],
            'res_model': 'account.invoice',
            'context': "{'type':'out_invoice'}",
            'type': 'ir.actions.act_window',
            'nodestroy': True,
            'target': 'current',
            'res_id': new_inv_ids and new_inv_ids[0] or False,
        }


    def action_cancel(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        sale_order_line_obj = self.pool.get('sale.order.line')
        account_invoice_obj = self.pool.get('account.invoice')
        for sale in self.browse(cr, uid, ids, context=context):
            for inv in sale.invoice_ids:
                if inv.state not in ('draft', 'cancel'):
                    raise osv.except_osv(
                        _('Cannot cancel this sales order!'),
                        _('First cancel all invoices attached to this sales order.'))
                inv.signal_workflow('invoice_cancel')
            sale_order_line_obj.write(cr, uid, [l.id for l in  sale.order_line],
                    {'state': 'cancel'})
        self.write(cr, uid, ids, {'state': 'cancel'})
        return True
    
    def action_view_invoice(self, cr, uid, ids, context=None):
        '''
        This function returns an action that display existing invoices of given sales order ids. It can either be a in a list or in a form view, if there is only one invoice to show.
        '''
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')

        result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree1')
        id = result and result[1] or False
        result = act_obj.read(cr, uid, [id], context=context)[0]
        #compute the number of invoices to display
        inv_ids = []
        for so in self.browse(cr, uid, ids, context=context):
            inv_ids += [invoice.id for invoice in so.invoice_ids]
        #choose the view_mode accordingly
        if len(inv_ids)>1:
            result['domain'] = "[('id','in',["+','.join(map(str, inv_ids))+"])]"
        else:
            res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')
            result['views'] = [(res and res[1] or False, 'form')]
            result['res_id'] = inv_ids and inv_ids[0] or False
        return result

    def test_no_product(self, cr, uid, order, context):
        for line in order.order_line:
            if line.product_id and (line.product_id.type<>'service'):
                return False
        return True

    
    def _amount_line_tax(self,cr, uid, line, context=None):
        val = 0.0
        tax_use = self.pool.get('account.tax')
        price_use = line.price_unit*(1-(line.discount or 0.0)/100.0)
        
        #for c in self.pool.get('account.tax').compute_all(cr,uid,line.tax_id,line.price_unit*(1-(line.discount or 0.0)/100.0),line.product_uom_qty,line.product_id,line.order_id.partner_id)['taxes']:
        for c in tax_use.compute_all(cr,uid,line.tax_id,price_use,line.product_uom_qty,line.product_id,line.order_id.partner_id)['taxes']: 
            val += c.get('amount','0.0')
        return val    
       

    def copy_quotation(self, cr, uid, ids, context=None):
        id = self.copy(cr, uid, ids[0], context=context)
        view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale', 'view_order_form')
        view_id = view_ref and view_ref[1] or False,
        return {
            'type': 'ir.actions.act_window',
            'name': _('Sales Order'),
            'res_model': 'yz.sale.order',
            'res_id': id,
            'view_type': 'form',
            'view_mode': 'form',
            'view_id': view_id,
            'target': 'current',
            'nodestroy': True,
        }
    
    def _get_order(self,cr,uid,ids,context=None):
        result = {}
        for line in self.pool.get('sale.order.line').browse(cr,uid,ids,context=context):
            result[line.order_id.id] = True
        return result.keys()
    
    
    
    #raise 弹窗
    def _get_default_company(self, cr, uid, context=None):
        company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
        if not company_id:
            raise osv.except_osv(_('Error!'), _('There is no default company for the current user!'))
        return company_id
    
    def _invoiced(self,cr,uid,ids,name,arg,context=None):
        res = {}
        for sale in self.browse(cr,uid,ids,context=context):
            res[sale.id] = True
            invoice_existence = False
            for invoice in sale.invoice_ids:
                if invoice.state != 'cancel':
                    invoice_existence = True
                    if invoice.state != 'paid':
                        res[sale.id] = False
                        break
            if not invoice_existence or sale.state == 'manual':
                res[sale.id] = False 
        return res      
    
    #need to study in the future
    def _invoiced_search(self,cr,uid,obj,name,args,context=None):
        if not len(args):
            return []
        clause = ''
        sale_clause = ''
        no_invoiced = False
        for arg in args:
            if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
                clause += 'AND inv.state = \'paid\''
            else:
                clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\'  AND inv.state <> \'paid\'  AND rel.order_id = sale.id '
                sale_clause = ',  yz_sale_order AS sale '
                no_invoiced = True

        cr.execute('SELECT rel.order_id ' \
                'FROM sale_order_invoice_rel AS rel, account_invoice AS inv '+ sale_clause + \
                'WHERE rel.invoice_id = inv.id ' + clause)
        res = cr.fetchall()
        if no_invoiced:
            cr.execute('SELECT sale.id ' \
                    'FROM yz_sale_order AS sale ' \
                    'WHERE sale.id NOT IN ' \
                        '(SELECT rel.order_id ' \
                        'FROM sale_order_invoice_rel AS rel) and sale.state != \'cancel\'')
            res.extend(cr.fetchall())
        if not res:
            return [('id', '=', 0)]
        return [('id', 'in', [x[0] for x in res])]   
    
    def _invoice_exists(self, cursor, user, ids, name, arg, context=None):
        res = {}
        for sale in self.browse(cursor, user, ids, context=context):
            res[sale.id] = False
            if sale.invoice_ids:
                res[sale.id] = True
        return res
    
    def _amount_all(self,cr,uid,ids,field_name,arg,context=None):
        cur_obj = self.pool.get('res.currency')
        res = {}
        for order in self.browse(cr,uid,ids,context=context):
            res[order.id] = {
                             'amount_untaxed': 0.0,
                             'amount_tax': 0.0,
                             'amount_total': 0.0,
                             }
            val = val1 = 0.0
            cur = order.pricelist_id.currency_id
            for line in order.order_line:
                val1 += line.price_subtotal
                val += self._amount_line_tax(cr, uid, line, context=context)
            res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val)
            res[order.id]['amount_untaxed'] = cur_obj.round(cr, uid, cur, val1)
            res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax']
        return res
    
    
    def _amount_all_wrapper(self,cr,uid,ids,field_name,arg,context=None):
        """ Wrapper because of direct method passing as parameter for function fields """    
        return self._amount_all(cr,uid,ids,field_name,arg,context=context)
    
   
    
    def _invoiced_rate(self,cr,uid,ids,name,arg,context=None):
        res = {}
        for sale in self.browse(cr,uid,ids,context=context):
            if sale.invoiced:
                res[sale.id] = 100.0
                continue
            tot = 0.0
            for invoice in sale.invoice_ids:
                if invoice.state not in ('draft','cancel'):
                    tot += invoice.amount_untaxed
            if tot:
                res[sale.id] = min(100.0,tot*100.0/(sale.amount_untaxed or 1.00))
            else:
                res[sale.id] = 0.0
        return res
    
    def _get_default_section_id(self, cr, uid, context=None):
        """ Gives default section by checking if present in the context """
        section_id = self._resolve_section_id_from_context(cr, uid, context=context) or False
        if not section_id:
            section_id = self.pool.get('res.users').browse(cr, uid, uid, context).default_section_id.id or False
        return section_id
    
    def _resolve_section_id_from_context(self,cr,uid,context=None):
        """ Returns ID of section based on the value of 'section_id'
            context key, or None if it cannot be resolved to a single
            Sales Team.
        """
        if context is None:
            context = {}
        if type(context.get('default_section_id')) in (int,long):
            return context.get('default_section_id')
        if isinstance(context.get('default_section_id'),basestring):
            section_ids = self.pool.get('crm.case.section').name_search(cr,uid,name=context['default_section_id'],context=context)
            if len(section_ids) ==1:
                return int(section_ids[0][0])
        return None
            
            
    _columns = {
                'name': fields.char('Order Reference', required=True, readonly=True, copy=False, select=True,states={'draft':[('readonly',False)],'sent':[('readonly',False)]}),
                'origin': fields.char(u'原单据', help="生成此销售订单的原单据"),
                'state': fields.selection([
                       ('draft', u'报价单草稿'),
                       ('sent', u'报价单已发送'),
                       ('cancel', u'取消'),
                       ('waiting_date', 'Waiting Schedule'),
                       ('progress', u'销售订单'),
                       ('manual', 'Sale to Invoice'),
                       ('shipping_except', u'发送异常'),
                       ('invoice_except', u'发票异常'),
                       ('done', u'已完成'),], u'状态', readonly=True, copy=False,
                          help="给出询价单或者销售订单的状态.\
                         \nThe exception status is automatically set when a cancel operation occurs \
                         in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception).\nThe 'Waiting Schedule' status is set when the invoice is confirmed\
                         but waiting for the scheduler to run on the order date.", select=True),
                
                 'date_order': fields.datetime(u'日期', required=True,select=True, copy=False,states={'draft':[('readonly',False)],'sent':[('readonly',False)]}),
                 'create_date':fields.datetime(u'创建日期',select=True,help='销售订单创建日期'),
                 'date_confirm': fields.date(u'确认日期', select=True, help="销售订单确认日期.", copy=False),
                 'user_id': fields.many2one('res.users', u'销售员',  select=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},track_visibility='onchange'),
                 'partner_id': fields.many2one('res.partner', u'客户', required=True, change_default=True, select=True,states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, track_visibility='always'),
                 'product_id': fields.many2one('product.product', u'产品', change_default=True, ondelect='restrict'),
                 'amount_untaxed': fields.function(_amount_all_wrapper,digits_compute=dp.get_precision('Account'),string='总计',
                                                 store={
                                                        'yz.sale.order':(lambda self,cr,uid,ids,c={}:ids,['order_line'],10),
                                                        'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
                                                        },multi = 'sums',help = '不含税金额',track_visibility='always'),
                 'amount_tax': fields.function(_amount_all_wrapper,digits_compute=dp.get_precision('Account'),string='总计',
                                                 store={
                                                        'yz.sale.order':(lambda self,cr,uid,ids,c={}:ids,['order_line'],10),
                                                        'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
                                                        },multi = 'sums',help = '税'),
                 'amount_total': fields.function(_amount_all_wrapper,digits_compute=dp.get_precision('Account'),string='总计',
                                                 store={
                                                        'yz.sale.order':(lambda self,cr,uid,ids,c={}:ids,['order_line'],10),
                                                        'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
                                                        },multi = 'sums',help = '总金额'),
                 'order_line': fields.one2many('sale.order.line', 'order_id', u'销售订单', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},copy=True),
                 'pricelist_id':fields.many2one('product.pricelist',u'价目表',  required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}),
                 'partner_invoice_id':fields.many2one('res.partner',u'发票地址',required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help='当前销售订单的发票地址'),
                 'partner_shipping_id':fields.many2one('res.partner',u'派送地址',required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},help='当前销售订单的派送地址'),
                 'payment_term':fields.many2one('account.payment.term',u'付款方式'),
                 'fiscal_position':fields.many2one('account.fiscal.position',u'财政状况'),
                 'company_id':fields.many2one('res.company',u'公司'),
                 'project_id':fields.many2one('account.analytic.account',u'协议/分析', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},help='与销售订单关联的分析账户'),
                
                 'currency_id':fields.related('pricelist_id','currency_id',type="many2one",relation="res.currency",string="货币",required=True),
                 'client_order_ref':fields.char(u'引用',copy=False),
                 'note': fields.text(u'条款和条约'),
                 'section_id':fields.many2one('crm.case.section', u'销售团队'),
                 'origin': fields.char(u'源文档', help="Reference of the document that generated this sales order request."),
                 'invoiced':fields.function(_invoiced,string='已付款',fnct_search=_invoiced_search,type='boolean',help='表明发票已支付'),
                 'invoice_ids':fields.many2many('account.invoice', 'sale_order_invoice_rel', 'order_id', 'invoice_id', u'发票', copy=False),
                 'invoice_exists': fields.function(_invoice_exists, string=u'发票',fnct_search=_invoiced_search, type='boolean', help="这表明销售订单指数有一张发票"),
                 'invoics_rate':fields.function(_invoiced_rate, string='发票比率', type='float'),
                 'order_policy': fields.selection([
                    ('manual', 'On Demand'),
                    ], u'创建发票', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
                    help="""This field controls how invoice and delivery operations are synchronized."""),
                'procurement_group_id': fields.many2one('procurement.group', 'Procurement group', copy=False),
                }
    
    _defaults = {
        'date_order': fields.datetime.now,
        'order_policy': 'manual',
        'company_id': _get_default_company,
        'state': 'draft',
        'user_id': lambda obj, cr, uid, context: uid,
        'name': lambda obj, cr, uid, context: '/',
        'partner_invoice_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['invoice'])['invoice'],
        'partner_shipping_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').address_get(cr, uid, [context['partner_id']], ['delivery'])['delivery'],
        'note': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.sale_note,
        'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
    }
    
    _sql_constraints = [
        ('name_uniq', 'unique(name, company_id)', 'Order Reference must be unique per Company!'),
    ]
    _order = 'date_order desc, id desc'
    
    def unlink(self,cr,uid,ids,context=None):
        sale_orders = self.read(cr,uid,ids,['state'],context=context)
        unlink_ids = []
        for s in sale_orders:
            if s['state'] in ['draft','cancel']:
                unlink_ids.appends(s['id'])
            else:
                raise osv.except_osv(_('Invalid Action'),_("In order to delete a confirmed sales order, you must cancel it before!"))
        return osv.osv.unlink(self,cr,uid,unlink_ids,context=context)
    
    def onchange_pricelist_id(self, cr, uid, ids, pricelist_id, order_lines, context=None):
        context = context or {}
        if not pricelist_id:
            return {}
        value = {
            'currency_id': self.pool.get('product.pricelist').browse(cr, uid, pricelist_id, context=context).currency_id.id
        }
        if not order_lines or order_lines == [(6, 0, [])]:
            return {'value': value}
        warning = {
            'title': _('Pricelist Warning!'),
            'message' : _('If you change the pricelist of this order (and eventually the currency), prices of existing order lines will not be updated.')
        }
        return {'warning': warning, 'value': value}
    
    def onchange_delivery_id(self,cr,uid,ids,company_id,partner_id,delivery_id,fiscal_position,context=None):
        res = {'value':{}}
        if not fiscal_position:
            if not company_id:
                company_id = self._get_default_company(cr,uid,context=context)
            fiscal_position = self.pool['account.fiscal.position'].get_fiscal_position(cr,uid,company_id,partner_id,delivery_id,context=context)
            if fiscal_position:
                res['value']['fiscal_position'] = fiscal_position
        return res
     
    def get_salenote(self,cr,uid,ids,partner_id,context=None):
        context_lang = context.copy()
        if partner_id:
            partner_lang = self.pool.get('res.partner').browse(cr,uid,partner_id,context=context).lang
            context_lang.update({'lang':partner_lang})
        return self.pool.get('res.users').browse(cr,uid,uid,context=context_lang).company_id.sale_note  
        
    #part  partner_id
    def onchange_partner_id(self,cr,uid,ids,part,context=None):
        if not part:
            return {'value':{'partner_invoice_id':False,'partner_shipping_id':False,'payment_term':False,'fiscal_position':False}}
        part = self.pool.get('res.partner').browse(cr,uid,part,context=context)
        addr = self.pool.get('res.partner').address_get(cr,uid,[part.id],['delivery','invoice','contact'])
        pricelist = part.property_product_pricelist and part.property_product_pricelist.id or False
        payment_term = part.property_payment_term and part.property_payment_term.id or False
        dedicated_salesman = part.user_id and part.user_id.id or False
        val = {
               'partner_invoice_id':addr['invoice'],
               'partner_shipping_id':addr['delivery'],
               'payment_term':payment_term,
               'user_id':dedicated_salesman,               
               }
        delivery_onchange = self.onchange_delivery_id(cr, uid, ids, False,part.id, addr['delivery'],False,context)
        val.update(delivery_onchange['value'])
        if pricelist:
            val['pricelist.id'] = pricelist
        sale_note = self.get_salenote(cr,uid,ids,part.id,context=context)
        if sale_note:
            val.update({'note':sale_note})
        return {'value':val}    
    
    #not understand
    def onchange_fiscal_position(self,cr,uid,ids,fiscal_position,order_lines,context=None):
        '''
         Update taxes of order lines for each line where a product is defined

        :param list ids: not used
        :param int fiscal_position: sale order fiscal position
        :param list order_lines: command list for one2many write method
        '''
        order_line = []
        fiscal_obj = self.pool.get('account.fiscal.position')
        product_obj = self.pool.get('product.product')
        line_obj = self.pool.get('sale.order.line')
        
        fpos = False
        if fiscal_position:
            fpos = fiscal_obj.browse(cr, uid, fiscal_position, context=context)
        
        for line in order_lines:
            # create    (0, 0,  { fields })
            # update    (1, ID, { fields })
            if line[0] in [0,1]:
                prod = None
                if line[2].get('product_id'):
                    prod = product_obj.browse(cr, uid, line[2]['product_id'], context=context)
                elif line[1]:
                    prod =  line_obj.browse(cr, uid, line[1], context=context).product_id
                if prod and prod.taxes_id:
                    line[2]['tax_id'] = [[6, 0, fiscal_obj.map_tax(cr, uid, fpos, prod.taxes_id)]]
                    order_line.append(line)
                    
            # link      (4, ID)
            # link all  (6, 0, IDS)
            elif line[0] in [4, 6]:
                line_ids = line[0] == 4 and [line[1]] or line[2]
                for line_id in line_ids:
                    prod = line_obj.browse(cr, uid, line_id, context=context).product_id
                    if prod and prod.taxes_id:
                        order_line.append([1, line_id, {'tax_id': [[6, 0, fiscal_obj.map_tax(cr, uid, fpos, prod.taxes_id)]]}])
                    else:
                        order_line.append([4, line_id])
            else:
                order_line.append(line)
        return {'value': {'order_line': order_line}}
                
    def create(self, cr, uid, vals, context=None):
        if context is None:
            context = {}
        if vals.get('name', '/') == '/':
            vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'yz.sale.order') or '/'
        if vals.get('partner_id') and any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id', 'fiscal_position']):
            defaults = self.onchange_partner_id(cr, uid, [], vals['partner_id'], context=context)['value']
            if not vals.get('fiscal_position') and vals.get('partner_shipping_id'):
                delivery_onchange = self.onchange_delivery_id(cr, uid, [], vals.get('company_id'), None, vals['partner_id'], vals.get('partner_shipping_id'), context=context)
                defaults.update(delivery_onchange['value'])
            vals = dict(defaults, **vals)
        ctx = dict(context or {}, mail_create_nolog=True)
        new_id = super(yz_sale_order, self).create(cr, uid, vals, context=ctx)
        self.message_post(cr, uid, [new_id], body=_("Quotation created"), context=ctx)
        return new_id
    
    def button_dummy(self, cr, uid, ids, context=None):
        return True
    
    # FIXME: deprecated method, overriders should be using _prepare_invoice() instead.
    #        can be removed after 6.1.
    def _inv_get(self, cr, uid, order, context=None):
        return {}

    def _prepare_invoice(self,cr,uid,order,lines,context=None):
        """
        为一个销售订单在创建新的发票时，准备字典类型的值。
        这个方法可以被重写来创建自定义的发票(确保调用super()方法，来建立一个易扩展的链).

           :param browse_record order: yz.sale.order record to invoice
                                     : yz.sale.order 发票记录
           :param list(int) line: list of invoice line IDs that must be attached to the invoice
                                ：必须与发票关联的发票行ID列表
           返回：创建发票时的字典值(:return: dict of value to create() the invoice)
        """
        if context is None:
            context = {}
        journal_ids = self.pool.get('account.journal').search(cr,uid,[('type','=','sale'),('company_id','=',order.company_id.id)],limit=1)
        if not journal_ids:
            raise osv.except_osv(_('Error!'), _('Please define sales journal for this company: "%s" (id:%d).') % (order.company_id.name, order.company_id.id))
        invoice_vals = {
                        'name':order.client_order_ref or '',
                        'origin':order.name,
                        'type':'out_invoice',
                        'reference':order.client_order_ref or order.name,
                        'account_id': order.partner_id.property_account_receivable.id,
                        'partner_id': order.partner_invoice_id.id,
                        'journal_id': journal_ids[0],
                        'invoice_line': [(6, 0, lines)],
                        'currency_id': order.pricelist_id.currency_id.id,
                        'comment': order.note,
                        'payment_term': order.payment_term and order.payment_term.id or False,
                        'fiscal_position': order.fiscal_position.id or order.partner_id.property_account_position.id,
                        'date_invoice': context.get('date_invoice', False),
                        'company_id': order.company_id.id,
                        'user_id': order.user_id and order.user_id.id or False,
                        'section_id' : order.section_id.id
                    }
        
        invoice_vals.update(self._inv_get(cr, uid, order, context=context))
        return invoice_vals
   
class sale_order_line(osv.osv):
    _name='sale.order.line'
    _description = '销售订单列表'
    
    def _amount_line(self,cr,uid,ids,field_name,arg,context=None):
        tax_obj = self.pool.get('account.tax')
        cur_obj = self.pool.get('res.currency')
        res = {}
        if context is None:
            context = {}
        for line in self.browse(cr,uid,ids,context=context):
            price = line.price_unit*(1-(line.discount or 0.0)/100.0)
            taxes = tax_obj.compute_all(cr,uid,line.tax_id,price,line.product_uom_qty,line.product_id,line.order_id.partner_id)
            cur = line.order_id.pricelist_id.currency_id
            res[line.id] = cur_obj.round(cr,uid,cur,taxes['total'])
        return res
    
    def _fnct_line_invoiced(self, cr, uid, ids, field_name, args, context=None):
        res = dict.fromkeys(ids, False)
        for this in self.browse(cr, uid, ids, context=context):
            res[this.id] = this.invoice_lines and \
                all(iline.invoice_id.state != 'cancel' for iline in this.invoice_lines) 
        return res
    
    def _order_lines_from_invoice(self, cr, uid, ids, context=None):
        # direct access to the m2m table is the less convoluted way to achieve this (and is ok ACL-wise)
        cr.execute("""SELECT DISTINCT sol.id FROM sale_order_invoice_rel rel JOIN
            sale_order_line sol ON (sol.order_id = rel.order_id) WHERE rel.invoice_id = ANY(%s)""", (list(ids),))
        return [i[0] for i in cr.fetchall()]
    
    _columns={
              'name': fields.text(u'描述', required=True,states={'draft': [('readonly', False)]}),
              'order_id': fields.many2one('yz.sale.order',u'订单编号',ondelete='cascade',select=True,states={'draft':[('readonly',False)]}),              
              'product_id': fields.many2one('product.product',u'产品',domain = [('sale_ok','=',True)],change_default=True,ondelect='restrict',states={'draft': [('readonly', False)]}),              
              'state':fields.selection([
                      ('cancel',u'已取消'),
                      ('draft',u'草稿'),
                      ('confirm',u'已确认'),
                      ('exception',u'异常'),
                      ('done',u'已完成'),],
                      u'状态',readonly=True,copy=False,
                      help='草稿：当销售订单是草稿状态时设定\
                            已确认：当销售订单是已确认状态时设定\
                            异常：当销售订单是异常状态时设定\
                            已完成：当销售订单是完成状态时设定\
                            已取消：当销售订单是取消状态时设定\ ',select=True),
              'price_unit':fields.float(u'单价',digits_compute=dp.get_precision('Product Price'),readonly=True,states={'draft': [('readonly', False)]}),
              'discount':fields.float(u'折扣',digits_compute=dp.get_precision('Discount'), readonly=True,states={'draft': [('readonly', False)]}),
              'price_subtotal':fields.function(_amount_line,string='小计',digits_compute=dp.get_precision('Account')),
              'tax_id':fields.many2many('account.tax','sale_order_tax','order_line_id','tax_id',u'税',readonly=True,states={'draft': [('readonly', False)]}),
              'product_uom_qty':fields.float(u'数量',digits_compute=dp.get_precision('product_Uos'),required=True,states={'draft': [('readonly', False)]}),
              'product_uom':fields.many2one('product.uom',u'计量单位',required=True, readonly=True, states={'draft': [('readonly', False)]}),
              'product_uos_qty':fields.float(u'数量（UoS）',digits_compute=dp.get_precision('product_Uos'),readonly = True,states={'draft': [('readonly', False)]}),
              'product_uos':fields.many2one('product.uom',u'产品UoS'),
              'th_weight': fields.float(u'重量', readonly=True, states={'draft': [('readonly', False)]}),
              'address_allotment_id': fields.many2one('res.partner', u'配送商家',help="特定产品的派送商家"),
              'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice', 'order_line_id', 'invoice_id', u'发货单', readonly=True, copy=False),
              'invoiced': fields.function(_fnct_line_invoiced, string='已支付', type='boolean',
                        store={
                               'account.invoice': (_order_lines_from_invoice, ['state'], 10),
                               'sale.order.line': (lambda self,cr,uid,ids,ctx=None: ids, ['invoice_lines'], 10)
                    }),
              'sequence': fields.integer(u'序列号', help="给出显示销售订单列表的序列号。"),
              'order_partner_id': fields.related('order_id', 'partner_id', type='many2one', relation='res.partner', store=True, string=u'客户'),
              'salesman_id':fields.related('order_id', 'user_id', type='many2one', relation='res.users', store=True, string=u'销售员'),
              'company_id':fields.related('order_id','company_id',type='many2one',relation='res.company',string='公司',store=True,readonly=True),
              'delay': fields.float('交货时间', required=True, help="订单的确认和产品发送给客户之间的天数", readonly=True, states={'draft': [('readonly', False)]}),
              'procurement_ids': fields.one2many('procurement.order', 'sale_line_id', u'采购'),
              }
    
    #values 的值是从页面传送过来的
    def create(self,cr,uid,values,context=None):
        if values.get('order_id') and values.get('product_id') and any(f not in values for f in ['name', 'price_unit', 'type', 'product_uom_qty', 'product_uom']):
            order = self.pool['yz.sale.order'].read(cr,uid,values['order_id'],['pricelist_id', 'partner_id', 'date_order', 'fiscal_position'], context=context)
            defaults = self.product_id_change(cr, uid, [], order['pricelist_id'][0], values['product_id'],
                qty=float(values.get('product_uom_qty', False)),
                uom=values.get('product_uom', False),
                qty_uos=float(values.get('product_uos_qty', False)),
                uos=values.get('product_uos', False),
                name=values.get('name', False),
                partner_id=order['partner_id'][0],
                date_order=order['date_order'],
                fiscal_position=order['fiscal_position'][0] if order['fiscal_position'] else False,
                flag=False,  # Force name update
                context=context
            )['value']
            
            if defaults.get('tax_id'):
                defaults['tax_id'] = [[6, 0, defaults['tax_id']]]
            values = dict(defaults, **values)
        return super(sale_order_line, self).create(cr, uid, values, context=context)
            
    def product_uom_change(self,cr,uid,ids,pricelist,product,qty=0,uom=False, qty_uos=0, uos=False, name='', partner_id=False,
            lang=False, update_tax=True, date_order=False, context=None):
        context = context or {}
        lang = lang or ('lang' in context and context['lang'])
        if not uom:
            return {'value': {'price_unit': 0.0, 'product_uom' : uom or False}}
        return self.product_id_change(cr, uid, ids, pricelist, product,
                qty=qty, uom=uom, qty_uos=qty_uos, uos=uos, name=name,
                partner_id=partner_id, lang=lang, update_tax=update_tax,
                date_order=date_order, context=context)
    
    
    def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
            lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
        context = context or {}
        lang = lang or context.get('lang', False)
        if not partner_id:
            raise osv.except_osv(_('No Customer Defined!'), _('Before choosing a product,\n select a customer in the sales form.'))
        warning = False
        product_uom_obj = self.pool.get('product.uom')
        partner_obj = self.pool.get('res.partner')
        product_obj = self.pool.get('product.product')
        context = {'lang': lang, 'partner_id': partner_id}
        partner = partner_obj.browse(cr, uid, partner_id)
        lang = partner.lang
        context_partner = {'lang': lang, 'partner_id': partner_id}

        if not product:
            return {'value': {'th_weight': 0,
                'product_uos_qty': qty}, 'domain': {'product_uom': [],
                   'product_uos': []}}
        if not date_order:
            date_order = time.strftime(DEFAULT_SERVER_DATE_FORMAT)

        result = {}
        warning_msgs = ''
        product_obj = product_obj.browse(cr, uid, product, context=context_partner)

        uom2 = False
        if uom:
            uom2 = product_uom_obj.browse(cr, uid, uom)
            if product_obj.uom_id.category_id.id != uom2.category_id.id:
                uom = False
        if uos:
            if product_obj.uos_id:
                uos2 = product_uom_obj.browse(cr, uid, uos)
                if product_obj.uos_id.category_id.id != uos2.category_id.id:
                    uos = False
            else:
                uos = False

        fpos = False
        if not fiscal_position:
            fpos = partner.property_account_position or False
        else:
            fpos = self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position)
        if update_tax: #The quantity only have changed
            result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id)

        if not flag:
            result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id], context=context_partner)[0][1]
            if product_obj.description_sale:
                result['name'] += '\n'+product_obj.description_sale
        domain = {}
        if (not uom) and (not uos):
            result['product_uom'] = product_obj.uom_id.id
            if product_obj.uos_id:
                result['product_uos'] = product_obj.uos_id.id
                result['product_uos_qty'] = qty * product_obj.uos_coeff
                uos_category_id = product_obj.uos_id.category_id.id
            else:
                result['product_uos'] = False
                result['product_uos_qty'] = qty
                uos_category_id = False
            result['th_weight'] = qty * product_obj.weight
            domain = {'product_uom':
                        [('category_id', '=', product_obj.uom_id.category_id.id)],
                        'product_uos':
                        [('category_id', '=', uos_category_id)]}
        elif uos and not uom: # only happens if uom is False
            result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id
            result['product_uom_qty'] = qty_uos / product_obj.uos_coeff
            result['th_weight'] = result['product_uom_qty'] * product_obj.weight
        elif uom: # whether uos is set or not
            default_uom = product_obj.uom_id and product_obj.uom_id.id
            q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom)
            if product_obj.uos_id:
                result['product_uos'] = product_obj.uos_id.id
                result['product_uos_qty'] = qty * product_obj.uos_coeff
            else:
                result['product_uos'] = False
                result['product_uos_qty'] = qty
            result['th_weight'] = q * product_obj.weight        # Round the quantity up

        if not uom2:
            uom2 = product_obj.uom_id
        # get unit price

        if not pricelist:
            warn_msg = _('You have to select a pricelist or a customer in the sales form !\n'
                    'Please set one before choosing a product.')
            warning_msgs += _("No Pricelist ! : ") + warn_msg +"\n\n"
        else:
            price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist],
                    product, qty or 1.0, partner_id, {
                        'uom': uom or result.get('product_uom'),
                        'date': date_order,
                        })[pricelist]
            if price is False:
                warn_msg = _("Cannot find a pricelist line matching this product and quantity.\n"
                        "You have to change either the product, the quantity or the pricelist.")

                warning_msgs += _("No valid pricelist line found ! :") + warn_msg +"\n\n"
            else:
                result.update({'price_unit': price})
        if warning_msgs:
            warning = {
                       'title': _('Configuration Error!'),
                       'message' : warning_msgs
                    }
        return {'value': result, 'domain': domain, 'warning': warning}
   
    def unlink(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        """允许删除在草稿、取消状态下的销售订单行"""
        for rec in self.browse(cr, uid, ids, context=context):
            if rec.state not in ['draft', 'cancel']:
                raise osv.except_osv(_('Invalid Action!'), _('Cannot delete a sales order line which is in state \'%s\'.') %(rec.state,))
        return super(sale_order_line, self).unlink(cr, uid, ids, context=context)
    
class account_invoice(osv.Model):
    _inherit="account.invoice"
    
    def _get_default_section_id(self,cr,uid,context=None):
        """ 通过检查当前的id是否在context里来给出默认销售团队ID """
        section_id = self._resolve_section_id_from_context(cr, uid, context=context) or False
        if not section_id:
            section_id = self.pool.get('res.users').browse(cr,uid,uid,context).default_section_id.id or False
        return section_id
    
    def _resolve_section_id_from_context(self,cr,uid,context=None):
        """ Returns ID of section based on the value of 'section_id'
            context key, or None if it cannot be resolved to a single
            Sales Team.
        """
        if context is None:
            context = {}
        if type(context.get('default_section_id')) in (int,long):
            return context.get('default_section_id')
        if isinstance(context.get('default_section_id'),basestring):
            section_ids = self.pool.get('crm.case.section').name_search(cr,uid,name=context['default_section_id'],context=context)
            if len(section_ids)==1:
                return int(section_ids[0][0])
        return None
    
    _columns={
              'section_id':fields.many2one('crm.case.section',u'销售团队'),
              }
    
    _defaults = {
        'section_id': lambda self, cr, uid, c=None: self._get_default_section_id(cr, uid, context=c)
    }
    
    
class procurement_order(osv.osv):
    _inherit = 'procurement.order'
    _columns = {
        'sale_line_id': fields.many2one('sale.order.line', string='销售订单列表'),
    } 
    
    def write(self, cr, uid, ids, vals, context=None):
        if isinstance(ids, (int, long)):
            ids = [ids]
        res = super(procurement_order, self).write(cr, uid, ids, vals, context=context)

        if vals.get('state') in ['done', 'cancel', 'exception']:
            for proc in self.browse(cr, uid, ids, context=context):
                if proc.sale_line_id and proc.sale_line_id.order_id:
                    order_id = proc.sale_line_id.order_id.id
                    if self.pool.get('yz.sale.order').test_procurements_done(cr, uid, [order_id], context=context):
                        workflow.trg_validate(uid, 'yz.sale.order', order_id, 'ship_end', cr)
                    if self.pool.get('yz.sale.order').test_procurements_except(cr, uid, [order_id], context=context):
                        workflow.trg_validate(uid, 'yz.sale.order', order_id, 'ship_except', cr)
        return res
    
class product_product(osv.Model):
    _inherit = 'product.product'
    
    def _sales_count(self,cr,uid,ids,field_name,arg,context=None):
        SaleOrderLine = self.pool['sale.order.line']
        return {
               product_id: SaleOrderLine.search_count(cr,uid,[('product_id','=',product_id)],context=context)
               for product_id in ids
               }
    
    _columns = {
        'sales_count': fields.function(_sales_count, string='# Sales', type='integer'),

    }

class product_template(osv.Model):
    _inherit = 'product.template' 
 
    def _sales_count(self, cr, uid, ids, field_name, arg, context=None):
        res = dict.fromkeys(ids, 0)
        for template in self.browse(cr, uid, ids, context=context):
            res[template.id] = sum([p.sales_count for p in template.product_variant_ids])
        return res
 
    def action_view_sales(self, cr, uid, ids, context=None):
        act_obj = self.pool.get('ir.actions.act_window')
        mod_obj = self.pool.get('ir.model.data')
        product_ids = []
        for template in self.browse(cr, uid, ids, context=context):
            product_ids += [x.id for x in template.product_variant_ids]
        result = mod_obj.xmlid_to_res_id(cr, uid, 'yz_sale.action_sale_order_line_product_tree',raise_if_not_found=True)
        result = act_obj.read(cr, uid, [result], context=context)[0]
        result['domain'] = "[('product_id','in',[" + ','.join(map(str, product_ids)) + "])]"
        return result
 
    _columns = {
        'sales_count': fields.function(_sales_count, string='# Sales', type='integer'),
    }